//Copyright 2008 Kristian Gundry And Rune Juhl-Petersen
//This file is part of Plain.

//Plain is free software: you can redistribute it and/or modify
//it under the terms of the GNU Lesser General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//any later version.

//Plain is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU Lesser General Public License for more details.

//You should have received a copy of the GNU Lesser General Public License
//along with Plain.  If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Web.UI;
using System.Reflection;
using System.Web.UI.WebControls;

namespace Plain.Web.Utilities
{
	/// <summary>
	/// A class used to calculate a checksum for a given control container.
	/// </summary>
	public class ClientComparer
	{
		/// <summary>
		/// Calculates a checksum based on the values of the controls in the given container
		/// </summary>
		/// <param name="container">a control or page containing controls</param>
		/// <returns>string checksum</returns>
		public static string CalculateChecksum(Control container)
		{
			string controlValues = string.Empty;
			GetControlValues(container, ref controlValues);
			return StringToMD5(controlValues);
		}

		private static void GetControlValues(Control container, ref string controlValues)
		{
			bool found = false;
			foreach(Control control in container.Controls)
			{
				if(control is ListControl)
				{
					ListControl listControl = (ListControl)control;
					controlValues += listControl.SelectedValue;
				}
				else if(control is MasterPage)
				{
					GetControlValues(control, ref controlValues);
				}
				else if(control is UserControl)
				{
					GetControlValues(control, ref controlValues);
				}
				else if(control != null && !(control is LiteralControl))
				{
					found = FindAndSetValue(ref controlValues, control, "Checked", typeof(bool));
					if(!found)
						found = FindAndSetValue(ref controlValues, control, "SelectedDate", typeof(DateTime));
					if(!found)
						found = FindAndSetValue(ref controlValues, control, "Values", typeof(string));
					if(!found)
						found = FindAndSetValue(ref controlValues, control, "Text", typeof(string));
					if(!found)
						GetControlValues(control, ref controlValues);
				}
			}
		}

		private static bool FindAndSetValue(ref string value, Control control, string propertyName, Type type)
		{
			PropertyInfo[] controlProperties = control.GetType().GetProperties();
			foreach(PropertyInfo propInfo in controlProperties)
			{
				if(propertyName == propInfo.Name && type == propInfo.PropertyType)
				{
					if(type != typeof(string))
					{
						if(propInfo.GetValue(control, null) != null)
							value += propInfo.GetValue(control, null).ToString();
					}
					else
					{
						if(propInfo.GetValue(control, null) != null)
							value += (string)propInfo.GetValue(control, null);
					}
					return true;
				}
			}
			return false;
		}

		private static string StringToMD5(string value)
		{
			MD5CryptoServiceProvider crypto = new MD5CryptoServiceProvider();
			byte[] bs = Encoding.UTF8.GetBytes(value);
			bs = crypto.ComputeHash(bs);
			StringBuilder checksumBuilder = new StringBuilder();
			foreach(byte b in bs)
			{
				checksumBuilder.Append(b.ToString("x2").ToLower());
			}
			return checksumBuilder.ToString();
		}
	}
}
